﻿using IOC.Annotation;
using IOC.Desgin;
using IOC.Proxy;
using IOC.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace IOC.Builder
{
   public class ContainerBuilder
    {
		private Dictionary<Type, RegisterEntity> RegistDic { get; set; }

		private Dictionary<Type, List<RegisterEntity>> RegistResultDic { get; set; }

		private Dictionary<Type, object> SingleInstanceDic { get; set; }

		private List<MutiRegisterEntity> MutiRegisterEntityList { get; set; }

		public ContainerBuilder()
		{
			RegistDic = new Dictionary<Type, RegisterEntity>();
			RegistResultDic = new Dictionary<Type, List<RegisterEntity>>();
			MutiRegisterEntityList = new List<MutiRegisterEntity>();
			SingleInstanceDic = new Dictionary<Type, object>();
		}

		public RegisterEntity RegisterType<T>()
		{
			RegisterEntity RE = new RegisterEntity(typeof(T));
			if (!RegistDic.Keys.Contains(RE.RegistType))
			{
				RegistDic.Add(typeof(T), RE);
				return RE;
			}
			throw new RepeatRegistException();
		}

		public MutiRegisterEntity RegisterAssemblyTypes(Assembly[] AssemblyList, Type BaseType, bool IsAttribute = false)
		{
			MutiRegisterEntity MutilEntity = new MutiRegisterEntity(AssemblyList, BaseType, IsAttribute);
			MutiRegisterEntityList.Add(MutilEntity);
			return MutilEntity;
		}

		public T Resolve<T>()
		{
			List<RegisterEntity> ValueList = RegistResultDic[typeof(T)];
			return (T)GetInstance(ValueList[ValueList.Count - 1]);
		}

		public T ResolveNamed<T>(string Name)
		{
			List<RegisterEntity> ValueList = RegistResultDic[typeof(T)].Where((RegisterEntity a) => a.Name == Name).ToList();
			return (T)GetInstance(ValueList[ValueList.Count - 1]);
		}

		public object Resolve(Type Type, CotainerEnum.TypeEqual TypeEqual = CotainerEnum.TypeEqual.Ref)
		{
			List<RegisterEntity> ValueList = null;
			if (TypeEqual == CotainerEnum.TypeEqual.Ref)
			{
				ValueList = RegistResultDic[Type];
			}
			else
			{
				foreach (KeyValuePair<Type, List<RegisterEntity>> KeyPair in RegistResultDic)
				{
					if (KeyPair.Key.FullName == Type.FullName)
					{
						ValueList = KeyPair.Value;
						break;
					}
				}
			}
			return GetInstance(ValueList[ValueList.Count - 1]);
		}

		public object ResolveGeneric(Type Type, Type[] GenericTypeArguments, CotainerEnum.TypeEqual TypeEqual = CotainerEnum.TypeEqual.Ref)
		{
			List<RegisterEntity> ValueList = null;
			if (TypeEqual == CotainerEnum.TypeEqual.Ref)
			{
				ValueList = RegistResultDic[Type];
			}
			else
			{
				foreach (KeyValuePair<Type, List<RegisterEntity>> KeyPair in RegistResultDic)
				{
					if (KeyPair.Key.FullName == Type.FullName)
					{
						ValueList = KeyPair.Value;
						break;
					}
				}
			}
			return GetInstance(ValueList[ValueList.Count - 1], GenericTypeArguments);
		}

		public List<Type> GetRegistType(Type Type)
		{
			List<Type> result = new List<Type>();
			foreach (Type item in RegistDic.Keys)
			{
				if (CompareUtil.IsAssignableFrom(item, Type))
				{
					result.Add(item);
				}
			}
			return result;
		}

		public bool Contains(Type Type)
		{
			return RegistResultDic.ContainsKey(Type);
		}

		private object GetInstance(RegisterEntity Entity, Type[] GenericTypeArguments = null)
		{
			object obj = null;
			if (Entity.IsEnableIntercept)
			{
				bool IsExtend = Entity.RealType == Entity.RegistType;
				obj = DynamictProxy.CreateProxyObject(Entity.RealType, Entity.RegistType, Entity.InterceptType, IsExtend, Entity.IsInterceptAllMethod);
			}
			else if (Entity.RegistType.IsGenericType && GenericTypeArguments != null)
			{
				Type GenericType = Entity.RegistType.MakeGenericType(GenericTypeArguments);
				obj = Activator.CreateInstance(GenericType);
			}
			else
			{
				ConstructorInfo[] constructors = Entity.RegistType.GetConstructors();
				obj = constructors[0].Invoke(new object[0]);
			}
			if (!SingleInstanceDic.ContainsKey(Entity.RealType))
			{
				SingleInstanceDic.Add(Entity.RealType, obj);
			}
			if (Entity.RealType.GetCustomAttribute(typeof(Component), inherit: true) != null)
			{
				foreach (FieldInfo Field in Entity.RealType.GetRuntimeFields())
				{
					if (Field.GetCustomAttribute(typeof(AutoWired), inherit: true) == null)
					{
						continue;
					}
					Type FieldType = Field.FieldType;
					if (!Contains(FieldType))
					{
						continue;
					}
					if (Entity.RegistType.IsGenericType)
					{
						Type GenericType = Entity.RegistType.MakeGenericType(GenericTypeArguments);
						if (SingleInstanceDic.ContainsKey(FieldType))
						{
							GenericType.InvokeMember(Field.Name, BindingFlags.SetField, null, obj, new object[1] { SingleInstanceDic[FieldType] });
						}
						else
						{
							GenericType.InvokeMember(Field.Name, BindingFlags.SetField, null, obj, new object[1] { Resolve(FieldType) });
						}
					}
					else if (SingleInstanceDic.ContainsKey(FieldType))
					{
						Field.SetValue(obj, SingleInstanceDic[FieldType]);
					}
					else
					{
						Field.SetValue(obj, Resolve(FieldType));
					}
				}
			}
			return obj;
		}

		public ContainerBuilder Build(CotainerEnum.BuidlModel Model = CotainerEnum.BuidlModel.NoRepeat)
		{
			foreach (MutiRegisterEntity mutiitem in MutiRegisterEntityList)
			{
				List<RegisterEntity> list = mutiitem.RegisterEntityList;
				foreach (RegisterEntity item in list)
				{
					if (!RegistDic.Keys.Contains(item.RegistType))
					{
						RegistDic.Add(item.RegistType, item);
						continue;
					}
					if (Model == CotainerEnum.BuidlModel.NoRepeat)
					{
						throw new RepeatRegistException();
					}
					RegistDic[item.RegistType] = item;
				}
			}
			RegisterEntity[] ValueList = new RegisterEntity[RegistDic.Values.Count];
			RegistDic.Values.CopyTo(ValueList, 0);
			List<RegisterEntity> ValueConvertList = ValueList.ToList();
			foreach (IGrouping<Type, RegisterEntity> group in from a in ValueConvertList
															  group a by a.RealType)
			{
				List<RegisterEntity> grouplist = new List<RegisterEntity>();
				foreach (RegisterEntity entity in group)
				{
					grouplist.Add(entity);
				}
				if (!RegistResultDic.ContainsKey(group.Key))
				{
					RegistResultDic.Add(group.Key, grouplist);
				}
				else
				{
					RegistResultDic[group.Key] = grouplist;
				}
			}
			return this;
		}
	}
}
